<?php

namespace App\WebSocket;


use App\Model\Baiyou\ApiAccessToken;
use App\Utils\LogHelp;
use App\Utils\RedisHelp;
use EasySwoole\EasySwoole\Config;

class WebSocketEvent
{
    /**
     * @param \Swoole\Http\Request $request
     * @param \Swoole\Http\Response $response
     * @return bool
     */
    public function onHandShake(\Swoole\Http\Request $request, \Swoole\Http\Response $response)
    {
        $time = time();
        LogHelp::info("{$request->fd}创建连接事件 : {$time}", 'websocket');

        /** 此处自定义握手规则 返回 false 时中止握手 */
        if (!$this->customHandShake($request, $response)) {
            $response->end();
            return false;
        }

        /** 此处是  RFC规范中的WebSocket握手验证过程 必须执行 否则无法正确握手 */
        if ($this->secWebsocketAccept($request, $response)) {
            $response->end();
            return true;
        }

        $response->end();
        return false;
    }

    /**
     * @param \Swoole\Http\Request $request
     * @param \Swoole\Http\Response $response
     * @return bool
     */
    protected function customHandShake(\Swoole\Http\Request $request, \Swoole\Http\Response $response): bool
    {
        /**
         * 这里可以通过 http request 获取到相应的数据
         * 进行自定义验证后即可
         * (注) 浏览器中 JavaScript 并不支持自定义握手请求头 只能选择别的方式 如get参数
         */
//        $headers = $request->header;
//        $cookie = $request->cookie;
        $get = $request->get;

        // 自定义校验【check_type: 1白友、2大屏】
//        $get['check_type'] = intval($get['check_type'] ?? 0);
//        $get['project_id'] = intval($get['project_id'] ?? 0);
//        if (isset($get['access_token']) === false || !in_array($get['check_type'], [1,2]) || empty($get['project_id'])) {
//            return false;
//        }
//
//        if ($get['check_type'] === 1) {// 白友
//            $token = $get['access_token'];
//            $timestamp = (int)substr($token, strrpos($token, '_') + 1);
//            $expire = Config::getInstance()->getConf("USER.accessTokenExpire");
//
//            // 验证有效期
//            if ($timestamp + $expire <= time()) {
//                LogHelp::waring("您的登录验证已经过期，请重新登录【token: {$token}】", 'websocket');
//                return false;
//            }
//
//            $accessToken = ApiAccessToken::create()->get([
//                'access_token' => $get['access_token'],
//            ]);
//            if (empty($accessToken)) {
//                LogHelp::waring("token不存在【token: {$get['access_token']}】", 'websocket');
//                return false;
//            }
//        } else {// 大屏永久有效
//            // TODO 大屏判断
//            /*$accessToken = ApiAccessToken::create()->get([
//                'access_token' => $get['access_token'],
//            ]);
//            if (empty($accessToken)) {
//                LogHelp::waring("token不存在【token: {$get['access_token']}】", 'websocket');
//                return false;
//            }*/
//        }

        // if (如果不满足我某些自定义的需求条件，返回false，握手失败) {
        //    return false;
        // }
        return true;
    }

    /**
     * 关闭事件
     * 所有客户端关闭时触发的方法
     * @param \swoole_server $server
     * @param int            $fd
     * @param int            $reactorId
     */
    public function onClose(\Swoole\WebSocket\Server $server, int $fd, int $reactorId)
    {
        /** @var array $info */
        $info = $server->getClientInfo($fd);

        /**
         * 判断此fd 是否是一个有效的 websocket 连接
         * 参见 https://wiki.swoole.com/wiki/page/490.html
         */
        if ($info && $info['websocket_status'] === WEBSOCKET_STATUS_FRAME)
        {
            // 清空pc通道缓存
            $redisHelp = new RedisHelp();
            $screenFd = $redisHelp->getScreenFd($fd);
            if ($screenFd) {
                $redisHelp->delScreenFd($fd);
                $redisHelp->delScreenProjectFdRem($screenFd['project_id'], $fd);
            }

            $time = time();
            LogHelp::info("{$fd}触发关闭事件 : {$time} ", 'websocket');

            /**
             * 判断连接是否是 server 主动关闭
             * 参见 https://wiki.swoole.com/wiki/page/p-event/onClose.html
             */
//            if ($reactorId < 0) {// 主动关闭
//                echo "server close \n";
//            }
        }
    }

    /**
     * RFC规范中的WebSocket握手验证过程
     * 以下内容必须强制使用
     *
     * @param \Swoole\Http\Request $request
     * @param \Swoole\Http\Response $response
     * @return bool
     */
    protected function secWebsocketAccept(\Swoole\Http\Request $request, \Swoole\Http\Response $response): bool
    {
        // ws rfc 规范中约定的验证过程
        if (!isset($request->header['sec-websocket-key'])) {
            // 需要 Sec-WebSocket-Key 如果没有拒绝握手
            LogHelp::debug('shake fail【sec-websocket-key empty】', 'shake');
            return false;
        }
        if (0 === preg_match('#^[+/0-9A-Za-z]{21}[AQgw]==$#', $request->header['sec-websocket-key'])
            || 16 !== strlen(base64_decode($request->header['sec-websocket-key']))
        ) {
            // 不接受握手
            LogHelp::debug('shake fail【sec-websocket-key error】', 'shake');
            return false;
        }

        $key = base64_encode(sha1($request->header['sec-websocket-key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
        $headers = array(
            'Upgrade' => 'websocket',
            'Connection' => 'Upgrade',
            'Sec-WebSocket-Accept' => $key,
            'Sec-WebSocket-Version' => '13',
            'KeepAlive' => 'off',
        );

        if (isset($request->header['sec-websocket-protocol'])) {
            $headers['Sec-WebSocket-Protocol'] = $request->header['sec-websocket-protocol'];
        }

        // 发送验证后的header
        foreach ($headers as $key => $val) {
            $response->header($key, $val);
        }

        // 接受握手 还需要101状态码以切换状态
        $response->status(101);
        return true;
    }
}
